#ifndef __GLF_FSTL_RINGBUFFER_H_INCLUDED__
#define __GLF_FSTL_RINGBUFFER_H_INCLUDED__

namespace glf {

template<typename T, uint SIZE>
class ring_buffer {
public:
	ring_buffer() {
		mHead = mSize = 0;
	}
	void push_back(const T& elem) {
		uint idx = GetIdx(mSize);
		
		mArray[idx] = elem;

		if(mSize == SIZE) {
			mHead = (mHead + 1) % SIZE; 
		} else {
			++mSize;
		}
	}

	void pop_back() {
		GLF_ASSERT(size() > 0);
		--mSize;
	}
	
	void pop_front() {
		GLF_ASSERT(size() > 0);

		mHead = (mHead + 1) % SIZE;
		--mSize;
	}

	T& GetRaw(uint idx) {
		return mArray[idx];
	}
	const T& GetRaw(uint idx) const {
		return mArray[idx];
	}
	T& operator[](uint idx) {
		return mArray[GetIdx(idx)];
	}
	const T& operator[](uint idx) const {
		return mArray[GetIdx(idx)];
	}
	const T& front() const {
		GLF_ASSERT(size() != 0);
		
		return mArray[GetIdx(0)];
	}
	T& front() {
		GLF_ASSERT(size() != 0);
		
		return mArray[GetIdx(0)];
	}
	const T& back() const {
		GLF_ASSERT(size() != 0);
		
		return mArray[GetIdx(mSize-1)];
	}
	T& back() {
		GLF_ASSERT(size() != 0);
	
		return mArray[GetIdx(mSize-1)];
	}

	void clear() {
		mHead = 0;
		mSize = 0;
	}
	uint size() const {
		return mSize;
	}
	bool empty() const {
		return mSize == 0;
	}
	uint capacity() const {
		return SIZE;
	}
	
	uint GetIdx(uint idx) const {
		idx = (mHead + idx) % SIZE;
		return idx;
	}
	uint GetHead() const {
		return mHead;
	}
private:
	uint mHead;
	uint mSize;
	T mArray[SIZE];
};

}

#endif
